package com.zxjbyte.yiyi.framework.common.core.okhttp;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.zxjbyte.yiyi.framework.common.exception.ServiceException;
import com.zxjbyte.yiyi.framework.common.util.JsonUtil;
import com.zxjbyte.yiyi.framework.common.util.ServletXUtil;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.Util;
import okio.BufferedSink;
import okio.Okio;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * okHttp 响应封装
 * @Author zhangxingjia
 * @Date 2023/4/24 10:55
 * @Version: 1.0
 */
@Slf4j
public class OkResponse implements IResponseProcessor {

    private final Request request;
    private final Response response;
    private final ResponseBody responseBody;

    public OkResponse(final Response response) {
        this.request = response.request();
        this.response = response;
        this.responseBody = bodyNullToEmpty(response.body());
    }

    public static OkResponse of(final Response response){
        return new OkResponse(response);
    }

    @Override
    public Request origRequest() {
        return this.request;
    }

    @Override
    public Response origResponse() {
        return this.response;
    }

    @Override
    public ResponseBody origResponseBody() {
        return this.responseBody;
    }

    @Override
    public int getStatus() {
        return response.code();
    }

    /**
     *  is success
     * @return
     */
    public boolean isOk(){
        return response.isSuccessful();
    }

    @Override
    public Headers responseHeaders() {
        return response.headers();
    }

    /**
     * Returns body String.
     * @return
     */
    public String asString(){
        return parseResponse(String.class);
    }

    /**
     * Returns body to byte arrays.
     * @return
     */
    public byte[] asBytes(){
        return parseResponse(byte[].class);
    }

    /**
     * Returns body to File.
     * @return
     */
    public File asFile(){
        return parseResponse(File.class);
    }

    public File asFile(String filePath){
        return asFile(FileUtil.file(filePath));
    }

    public File asFile(File targetFile){
        return parseResponse(File.class, targetFile);
    }

    /**
     * Returns body to InputStream.
     * @return
     */
    public InputStream asStream(){
        return parseResponse(InputStream.class);
    }


    /**
     * 转换为指定类型的对象
     *
     * @param targetType 目标类型Class
     */
    public <T> T toObject(Class<T> targetType) {
        return parseResponse(targetType);
    }

    /**
     * 转换为复杂泛型类型的对象
     *
     * @param typeReference 类型引用
     */
    public <T> T toObject(TypeReference<T> typeReference) {
        return parseResponse(typeReference.getType());
    }

    // 保持专门的JSON节点方法
    public JsonNode toJsonNode() {
        return parseResponse(JsonNode.class);
    }


    private ResponseBody bodyNullToEmpty(ResponseBody body) {
        return body == null ? Util.EMPTY_RESPONSE : body;
    }

    @SneakyThrows
    private <T> T parseResponse(Type returnType, Object... params) {
        try {
            if (returnType == null || response.code() == 204) {
                // 如果未定义returnType，或者状态代码为204（无内容），则返回null
                responseBody.close();
                return null;
            }
            // 验证响应状态
            if (!isOk()) {
                throw new ServiceException("HTTP request failed with code: " + response.code());
            }

            // 处理特定返回类型
            if (isByteArrayType(returnType)) {
                return (T) responseBody.bytes();
            } else if (isFileType(returnType)) {
                return (T) downloadToFile(params);
            } else if (isInputStreamType(returnType)) {
                return (T) responseBody.byteStream();
            }

            // 处理文本和JSON类型
            return parseTextContent(returnType);
        } finally {
            // 对于非流类型，确保响应体被关闭
            if (!isInputStreamType(returnType)) {
                closeQuietly();
            }
        }
    }

    /**
     * 解析文本内容（字符串或JSON）
     */
    @SuppressWarnings("unchecked")
    @SneakyThrows
    private <T> T parseTextContent(Type returnType) {
        String contentType = getContentType();
        String responseContent = responseBody.string();

        // 字符串类型直接返回
        if (isStringType(returnType)) {
            return (T) responseContent;
        }

        // JSON类型处理
        if (ServletXUtil.isJsonMime(contentType)) {
            if (returnType.equals(JsonNode.class)) {
                return (T) JsonUtil.readTree(responseContent);
            } else {
                return JsonUtil.parseObject(responseContent, returnType);
            }
        }

        // 纯文本类型尝试解析
        if (contentType.startsWith("text/plain")) {
            if (isSimpleType(returnType)) {
                return JsonUtil.parseObject(responseContent, returnType);
            }
            throw new ServiceException("Plain text response cannot be converted to: " + returnType);
        }

        throw new ServiceException("Content type " + contentType + " is not supported for type: " + returnType);
    }

    // ==================== 文件下载相关方法 ====================

    /**
     * 下载文件
     */
    private File downloadToFile(Object[] params) throws IOException {
        File file = prepareTargetFile(params);
        BufferedSink sink = Okio.buffer(Okio.sink(file));
        // 将响应体中的数据写入文件
        sink.writeAll(responseBody.source());
        sink.close();
        return file;
    }


    private File prepareTargetFile(Object[] params) {
        if (params != null && params.length > 0) {
            return (File) params[0];
        }
        String filename = null;
        String contentDisposition = response.header("Content-Disposition");
        if (StrUtil.isNotBlank(contentDisposition)) {
            // Get filename from the Content-Disposition header.
            Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?");
            Matcher matcher = pattern.matcher(contentDisposition);
            if (matcher.find()) {
                filename = matcher.group(1).replaceAll(".*[/\\\\]", "");
            }
        }
        String prefix = null;
        String suffix = null;
        if (filename == null) {
            prefix = "download-";
            suffix = "";
        } else {
            int pos = filename.lastIndexOf(".");
            if (pos == -1) {
                prefix = filename + "-";
            } else {
                prefix = filename.substring(0, pos) + "-";
                suffix = filename.substring(pos);
            }
            // Files.createTempFile requires the prefix to be at least three characters long
            if (prefix.length() < 3) {
                prefix = "download-";
            }
        }
        return FileUtil.createTempFile(prefix, suffix, true);
    }


    // ==================== 类型检查辅助方法 ====================

    private boolean isByteArrayType(Type type) {
        return type instanceof Class && byte[].class.equals(type);
    }

    private boolean isFileType(Type type) {
        return type instanceof Class && File.class.equals(type);
    }

    private boolean isInputStreamType(Type type) {
        return type instanceof Class && InputStream.class.equals(type);
    }

    private boolean isStringType(Type type) {
        return type instanceof Class && String.class.equals(type);
    }

    private boolean isSimpleType(Type type) {
        return type instanceof Class &&
            (String.class.equals(type) ||
                Number.class.isAssignableFrom((Class<?>) type) ||
                Boolean.class.equals(type) ||
                ((Class<?>) type).isPrimitive());
    }

    /**
     * 获取Content-Type，默认为application/json
     */
    private String getContentType() {
        String contentType = response.headers().get("Content-Type");
        return StrUtil.isNotBlank(contentType) ? contentType : "application/json";
    }

    /**
     * 类型引用（用于泛型类型）
     */
    @Getter
    public abstract static class TypeReference<T> {
        private final Type type;

        protected TypeReference() {
            Type superClass = getClass().getGenericSuperclass();
            if (superClass instanceof Class) {
                throw new IllegalArgumentException("TypeReference must be created with type parameter");
            }
            this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
        }

    }

    /**
     * 安静关闭响应体
     */
    public void closeQuietly() {
        Util.closeQuietly(this.responseBody);
    }


    @Override
    public void close() throws IOException {
        closeQuietly();
    }

}
